#!/usr/bin/python -t
# -*- coding: utf-8 -*-
#
# $Id: lokatin-fso-special 80 2009-08-11 20:09:35Z jozo $

__program_name__ = 'youloc-gypsy'
__version__      = '0.0.0'
__revision___    = "$Id: lokatin-fso-special 80 2009-08-11 20:09:35Z jozo $"
__author__       = "Kai Kasurinen"
__email__        = "kai.kasurinen@uninea.fi"
__copyright__    = "Copyright 2009, Kai Kasurinen"

import os
import sys
import optparse
import logging
import gobject
import dbus
import dbus.mainloop.glib
import time
import httplib, urllib, urllib2

if sys.version_info < (2, 6):
    sys.exit('Your python interpreter is too old. Please consider upgrading.')

# FIXME:
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

def dbus_to_python(v):
    if isinstance(v, dbus.Int32) or type(v) == types.IntType:
        return int(v)
    elif isinstance(v, dbus.Double) or type(v) == types.FloatType:
        return float(v)
    elif isinstance(v, dbus.String) or type(v) == types.StringType:
        return str(v)
    elif isinstance(v, dbus.Dictionary) or type(v) == types.DictType:
        return dict( (dbus_to_python(k), dbus_to_python(v)) for k,v in v.iteritems() )
    elif isinstance(v, dbus.Array) or type(v) == types.ListType:
        return [dbus_to_python(x) for x in v]
    elif isinstance(v, dbus.Struct) or type(v) == types.TupleType:
        return tuple(dbus_to_python(x) for x in v)
    elif isinstance(v, dbus.Boolean) or type(v) == types.BooleanType:
        return bool(v)
    else:
        return v
        raise TypeError("Can't convert type %s to python object" % type(v))

class _ProxyObject(dbus.proxies.ProxyObject):

    def __init__(self, dbus_connection=None,
                       dbus_bus_name=None,
                       dbus_object_path=None,
                       introspect=True, follow_name_owner_changes=True):

        if dbus_connection is not None:  self._dbus_connection=dbus_connection
        if dbus_bus_name is not None:    self._dbus_bus_name=dbus_bus_name
        if dbus_object_path is not None: self._dbus_object_path=dbus_object_path

        dbus.proxies.ProxyObject.__init__(
            self,
            self._dbus_connection, self._dbus_bus_name, self._dbus_object_path,
            follow_name_owner_changes=True)
        logging.debug(self)

    def get_interface(self, dbus_interface):
        logging.debug('get interface: %s ' % dbus_interface)
        return dbus.Interface(self, dbus_interface)

# END _ProxyObject class

class Gypsy(_ProxyObject):
    _dbus_connection  = dbus.SystemBus()
    _dbus_bus_name    = 'org.freedesktop.Gypsy'
    _dbus_object_path = '/org/freedesktop/Gypsy'
    
    def __init__(self):
        _ProxyObject.__init__(self)
        self.Server = self.get_interface('org.freedesktop.Gypsy.Server')

# END Gypsy class

class GypsyClient(_ProxyObject):
    _dbus_connection  = dbus.SystemBus()
    _dbus_bus_name    = 'org.freedesktop.Gypsy'
    
    def __init__(self, device=''):
        dbus_object_path = Gypsy().Server.Create(device)
        _ProxyObject.__init__(self, dbus_object_path=dbus_object_path)

        self.Accuracy = self.get_interface('org.freedesktop.Gypsy.Accuracy')
        self.Course = self.get_interface('org.freedesktop.Gypsy.Course')
        self.Device = self.get_interface('org.freedesktop.Gypsy.Device')
        self.Position = self.get_interface('org.freedesktop.Gypsy.Position')
        self.Satellite = self.get_interface('org.freedesktop.Gypsy.Satellite')
        self.Time = self.get_interface('org.freedesktop.Gypsy.Time')

# END GypsyClient class

class PositionUpdater(object):
    api_url = 'http://youloc.net/logger.php'

    def __init__(self, user_key=None, update_interval=10):
        self.user_key=user_key
        if self.user_key is None:
            raise 'No user_key'
        self.update_interval = update_interval
        self.opener = urllib2.build_opener()
        self.opener.addheaders = [('User-agent', ('Lokatin/%s/%s' % (__version__, __program_name__)))]
        self._last_kwargs = {'lat':0,'lon':0,'time':time.time()}

    def _post_data(self, kwargs):
        """
        userkey, username, imei (?),
        lat (latitude), lon (longitude), alt (altitude),
        status, speed, course, heading (?), time (?), date (?), hdop,
        cell, lac, nwname, mcc, mnc, ssi
        """
        
        kwargs.update({
            'userkey':self.user_key
        })

        url = self.api_url + '?' + urllib.urlencode(kwargs)
        logging.debug('GET %s' % url)

        try:
            u = self.opener.open(url, None, 10)
        except urllib2.HTTPError as e:
            logging.error('%s: %s' % (e, e.readline()))

    def feed_me(self, **kwargs):
        """"""
        kwargs.update({
            'time':time.time()
        })
        if (kwargs['lat'] == self._last_kwargs['lat'] or
            kwargs['lon'] == self._last_kwargs['lon']):
            return
        if (kwargs['time'] < self._last_kwargs['time'] + self.update_interval):
            return
        self._last_kwargs = kwargs
        self._post_data(kwargs)

# END PositionUpdater class


class Mainio(object):

    def __init__(self, options=None, args=None):
        dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
        self.main_loop = gobject.MainLoop()
        logging.debug('main loop: %s ' % dbus.get_default_main_loop())
        self.init_gypsy(options.gypsy_device)
        logging.debug('using YouLoc key: %s' % args[0])
        self.youloc = PositionUpdater(user_key=args[0],
            update_interval=options.update_interval)

    def init_gypsy(self, device=''):
        logging.debug('initing gypsy client...')
        self.gypsy = GypsyClient(device=device)
        self.gypsy.Start()
        self.gypsy.Device.connect_to_signal('ConnectionStatusChanged', self._connection_status_changed)
        self.gypsy.Device.connect_to_signal('FixStatusChanged', self._fix_status_changed)
        self.gypsy.Position.connect_to_signal('PositionChanged', self._position_changed)
        self.gypsy.Accuracy.connect_to_signal('AccuracyChanged', self._accuracy_changed)
        self.gypsy.Course.connect_to_signal('CourseChanged', self._course_changed)
        self._gypsy_log_status()

    def _gypsy_log_status(self):
        # FIXME:
        try:
            logging.debug('GPS connection: %s, fix: %s' % (
                self.gypsy.GetConnectionStatus(),
                self.gypsy.GetFixStatus()
            ))
        except:
            pass

    def _connection_status_changed(self, status):
        logging.info('GPS connection status changed: %s' % status)

    def _fix_status_changed(self, status):
        logging.info('GPS fix status changed: %s' % status)

    def _position_changed(self, fields, timestamp, latitude, longitude, altitude, accuracy=None):
        logging.debug('%d: %2f, %2f (%1fm)' % (timestamp, latitude, longitude, altitude))
        self.youloc.feed_me(lat=latitude, lon=longitude, alt=altitude, time=dbus_to_python(timestamp))

    def _accuracy_changed(self, fields, pdop, hdop, vdop):
        logging.debug('GPS accuracy changed: pdop %s, hdop %s, vdop %s' % (pdop, hdop, vdop))

    def _course_changed(self, fields, tstamp, speed, heading, climb):
        logging.debug('GPS course changed: speed %s, heading %s, climb %s' % (speed, heading, climb))

    def run(self):
        logging.debug("entering mainloop")
        self.main_loop.run()

    def stop(self):
        logging.debug("exiting mainloop")
        self.main_loop.quit()


# END Mainio class

def main(options=None, args=None):
    logging.basicConfig(level=options.loglevel,
                        format='%(asctime)s %(levelname)s %(message)s')

    logging.info('%s %s' % (__program_name__, __version__))

    mainio = Mainio(options=options, args=args)
    try:
        mainio.run()
    except KeyboardInterrupt:
        mainio.stop()

# END main def

if  __name__ == '__main__':
    parser = optparse.OptionParser(
        version='%s %s' % (__program_name__, __version__),
        usage = 'usage: %prog [options] youloc-key')
    parser.add_option('', '--gypsy-device',
        dest = 'gypsy_device', default='',
        help = '')
    parser.add_option('-L', '--loglevel',
        dest='loglevel', default=logging.DEBUG, type='int',
        help='set verbosity to LEVEL - DEBUG 10, INFO 20 ...')
    parser.add_option('', '--update-interval',
        dest='update_interval', default=10, type='int',
        help='set update interval')

    (options, args) = parser.parse_args()

    if len(args) == 0:
        parser.print_usage()
        sys.exit(1)

    main(options=options, args=args)
